package com.lanchetech.user.service.impl;


import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.baidu.fsg.uid.UidGenerator;
import com.lanchetech.bean.request.LoginReq;
import com.lanchetech.bean.request.RegisterReq;
import com.lanchetech.bean.request.UpdatePasswordByPhoneReq;
import com.lanchetech.bean.response.BasePageResp;
import com.lanchetech.bean.response.BaseResp;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.bean.response.SpuResp;
import com.lanchetech.common.enums.*;
import com.lanchetech.common.util.EmojiFilter;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.service.CardConsumptionFundService;
import com.lanchetech.service.ConsumptionService;
import com.lanchetech.service.LevelService;
import com.lanchetech.service.SmsService;
import com.lanchetech.user.config.JwtTokenUtil;
import com.lanchetech.user.service.AuthService;
import com.lanchetech.user.service.MiniappService;
import com.lanchetech.user.service.UserService;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import jodd.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class AuthServiceImpl implements AuthService {

    private AuthenticationManager authenticationManager;
    private UserDetailsService userDetailsService;
    private JwtTokenUtil jwtTokenUtil;
    private UserMapper userMapper;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    private CardConsumptionFundService cardConsumptionFundService;

    @Autowired
    private UserRoleMapper userRoleDAO;

    @Autowired
    DefaultKaptcha defaultKaptcha;

    @Autowired
    SmsService smsService;

    @Autowired
    MiniappService miniappService;

    @Autowired
    UserInfoMapper userInfoMapper;

    @Autowired
    UserService userService;

    @Autowired
    AreaMapper areaMapper;

    @Autowired
    ConsumptionService consumptionService;

    @Autowired
    ConsumptionRecordMapper consumptionRecordMapper;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    MsgUserMapper msgUserMapper;

    @Resource
    UidGenerator uidGenerator;

    @Autowired
    UserAgentLevelMapper userAgentLevelMapper;

    @Autowired
    LevelService levelService;

    @Autowired
    LotteryMapper lotteryMapper;

    @Autowired
    LotteryOpportunityMapper lotteryOpportunityMapper;


    @Autowired
    public AuthServiceImpl(
            AuthenticationManager authenticationManager,
            @Qualifier("customUserService") UserDetailsService userDetailsService,
            JwtTokenUtil jwtTokenUtil,
            UserMapper UserMapper) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.userMapper = UserMapper;
    }


    @Override
    public ResultData<String> register(RegisterReq req) {
        ResultData<String> resultData = new ResultData<>();
        User user = new User();

        // 验证码是否正确
        if (!smsService.verifySms(req.getUsername(), req.getCode())) {
            resultData.setResultCode(ResultCode.SMS_CODE_WRONG);
            return resultData;
        }

        String openid = miniappService.getUserOpenid(req.getJscode());
        if (openid == null) {
            resultData.setResultCode(ResultCode.GET_OPENID_FAIL);
            return resultData;
        }

        user.setUsername(req.getUsername());
        user.setOpenid(openid);
        user.setFromChannel(FromChannelEnum.MINI_PROGRAM.getType());
        return commonRegister(user, req);
    }

    private ResultData<String> commonRegister(User user, RegisterReq req) {
        ResultData<String> resultData = new ResultData<>();

        if (isUserExists(user)) {
            resultData.setResultCode(ResultCode.USER_EXISTED);
            return resultData;
        }

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        final String rawPassword = UUID.randomUUID().toString();
        user.setPassword(encoder.encode(rawPassword));
        user.setLastPasswordResetDate(new Date());
        // 过滤表情
        user.setNickname(EmojiFilter.filterEmoji(req.getNickname()));
        user.setAvatar(req.getAvatar());
        user.setOpenid(user.getOpenid());
        user.setStatus(UserStatusEnum.NORMAL.getStatus());
        // 初始化数值
        BigDecimal zero = new BigDecimal(0);
        user.setCoin(zero);
        user.setPoint(zero);
        user.setCardPoint(zero);
        user.setAdPoint(zero);
        user.setExp(0);
        user.setLevel(0);
        user.setCreatedAt(new Date());
        user.setUpdatedAt(new Date());
        user.setRegisterId(req.getRegisterId());
        user.setStaffType(UserStaffTypeEnum.NOT_STAFF.getType());
        userMapper.insert(user);

        // 插入用户微信信息
        UserInfo userInfo = UserInfo.builder()
                .userId(user.getId())
                .province(req.getProvince())
                .city(req.getCity())
                .gender(req.getGender())
                .createdAt(new Date())
                .updatedAt(new Date())
                .fansCount(0L)
                .attentionCount(0L)
                .anchorLevel(AnchorLevelEnum.NOT_ANCHOR.getLevel())
                .build();

        userInfoMapper.insert(userInfo);
        // 绑定邀请关系
        if (req.getInviteCode() != null && req.getInviteCode().length() >= 6) {
            userService.bindInviteCode(user.getId(), req.getInviteCode(), user.getUsername());
        }

        //添加用户角色
        userRoleDAO.insert(UserRole.builder()
                .userId(user.getId())
                .roleId(UserRoleEnum.ROLE_CUSTOMER.getId())
                .createdAt(new Date())
                .build());

        // 创建聊天用户
        msgUserMapper.insert(MsgUser.builder()
                .username(Long.toString(uidGenerator.getUID()))
                .password(encoder.encode(Long.toString(uidGenerator.getUID())))
                .lastPasswordResetDate(new Date())
                .status(MsgUserStatusEnum.IN_SERVICE.getStatus())
                .relateId(user.getId())
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                .role(MsgUserRoleEnum.USER.getRole())
                .tenantId(null)
                .createdAt(new Date())
                .updatedAt(new Date())
                .deleted(DeletedEnum.DEFAULT.getStatus())
                .build());

        updateMsgUserRegisterId(user.getId(), req.getRegisterId());


        // 新注册的用户直接赠送初级会员
        levelService.addLowMember(user);

        // 新注册的用户直接赠送消费金
        CardConsumptionFund card = card(user);
        req.setCardConsumptionFund(card);

        //赠送抽奖机会
        Lottery lottery = lotteryMapper.findOneByStatusAndDeletedAndType(ToggleEnum.ON.getStatus(), DeletedEnum.DEFAULT.getStatus(), CoinProductTypeEnum.NINE_LOTTERY.getType(), new Date());
        if (null != lottery) {
            LotteryOpportunity opportunity = LotteryOpportunity.builder()
                    .userId(user.getId())
                    .relateId(0L)
                    .type(LotteryOpportunityTypeEnum.SPU_GIVE.getType())
                    .status(LotteryOpportunityStatusEnum.NOT_USED.getStatus())
                    .createdAt(new Date())
                    .updatedAt(new Date()).build();
            lotteryOpportunityMapper.insert(opportunity);
        }

        // 直接生成token登录
        final Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        final String token = jwtTokenUtil.generateToken(user);
        resultData.setResultCode(ResultCode.SUCCESS);
        resultData.setValue(token);
        return resultData;
    }

    private CardConsumptionFund card(User user) {
        CardConsumptionFund cardConsumptionFund = cardConsumptionFundService.findOne();
        if (cardConsumptionFund != null) {
            CardConsumptionUser cardConsumptionUser = cardConsumptionFundService.getCardConsumptionUser(user.getId());
            if (cardConsumptionUser == null) {
                // 生成用户消费金卡
                CardConsumptionUser consumptionUser = new CardConsumptionUser();
                consumptionUser.setCardConsumptionFundId(cardConsumptionFund.getId());
                consumptionUser.setUserId(user.getId());
                consumptionUser.setAmt(cardConsumptionFund.getCardQuota());
                consumptionUser.setAfterAmt(BigDecimal.ZERO);
                consumptionUser.setBeforeAmt(BigDecimal.ZERO);
                consumptionUser.setCreated(new Date());
                cardConsumptionFundService.insertCardConsumptionUser(consumptionUser);
            }
            return cardConsumptionFund;
        }
        return new CardConsumptionFund();
    }

    private Boolean isUserExists(User user) {
        User usernameExists = userMapper.findOneByUsername(user.getUsername());
        User openidExists = userMapper.findOneByOpenid(user.getOpenid());
        return usernameExists != null || openidExists != null;
    }

    /**
     * 更新消息用户的registerId
     */
    void updateMsgUserRegisterId(Long userId, String registerId) {
        MsgUser msgUser = msgUserMapper.findOneByRelateIdAndRole(userId, MsgUserRoleEnum.USER.getRole());
        if (msgUser != null) {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            String key = "MsgRid:" + msgUser.getId();
            operations.set(key, registerId);
        }
    }

    @Override
    public ResultData<String> login(LoginReq req) {
        ResultData<String> resultData = new ResultData<>();
        try {
            if (smsService.verifySms(req.getUsername(), req.getCode())) {
                User user = userMapper.findOneByUsername(req.getUsername());

                if (user != null && user.getOpenid() == null && req.getJscode() != null) {
                    String openid = miniappService.getUserOpenid(req.getJscode());
                    user.setOpenid(openid);
                    userMapper.updateByPrimaryKeySelective(user);
                }


                final Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
                SecurityContextHolder.getContext().setAuthentication(authentication);
                final String token = jwtTokenUtil.generateToken(user);
                resultData.setResultCode(ResultCode.SUCCESS);
                resultData.setValue(token);


                if (!StringUtil.isEmpty(req.getRegisterId()) && !req.getRegisterId().equals(user.getRegisterId())) {
                    this.userMapper.updateRegisterIdById(req.getRegisterId(), user.getId());
                    updateMsgUserRegisterId(user.getId(), req.getRegisterId());
                }


                if (req.getType() != null) {
                    // 伪登录更新
                    ValueOperations<String, Boolean> operations = redisTemplate.opsForValue();
                    // 进行商家登录
                    if (PayLoginTypeEnum.SHOP.getType().equals(req.getType())) {

                        if (user.getShopId() == null) {
                            resultData.setResultCode(ResultCode.DO_NOT_HAVE_SHOP);
                        } else {
                            String key = "SHOP_SECURITY_LOGIN:" + user.getId();
                            operations.set(key, true, 1, TimeUnit.DAYS);
                        }
                    }

                    if (PayLoginTypeEnum.AREA.getType().equals(req.getType())) {

                        List<UserAgentLevel> userAgentLevels = userAgentLevelMapper.findAllByUserIdAndExpireTime(user.getId(), new Date());

                        if (userAgentLevels.isEmpty()) {
                            resultData.setResultCode(ResultCode.DO_NOT_HAVE_AREA);
                        } else {
                            String key = "AREA_SECURITY_LOGIN:" + user.getId();
                            operations.set(key, true, 1, TimeUnit.DAYS);
                        }
                    }
                }
                // 赠送消费金
                card(user);
            } else {
                resultData.setResultCode(ResultCode.SMS_CODE_WRONG);
            }
        } catch (Exception e) {
            resultData.setResultCode(ResultCode.USERNAME_OR_PASSWORD_ERROR);
        }
        return resultData;
    }

    @Override
    public ResultData<String> loginByPassword(LoginReq req) {
        try {
            UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(req.getUsername(), req.getPassword());
            final Authentication authentication = authenticationManager.authenticate(upToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            User user = this.userMapper.findOneByUsername(req.getUsername());

            user.setPassword(null);
            final UserDetails userDetails = user;
            final String token = jwtTokenUtil.generateToken(userDetails);

            if (!StringUtil.isEmpty(req.getRegisterId()) && !req.getRegisterId().equals(user.getRegisterId())) {
                this.userMapper.updateRegisterIdById(req.getRegisterId(), user.getId());
                updateMsgUserRegisterId(user.getId(), req.getRegisterId());
            }
            // 赠送消费金
            card(user);
            return new ResultData<>(token);
        } catch (Exception e) {
            ResultData resultData = new ResultData();
            resultData.setResultCode(ResultCode.USERNAME_OR_PASSWORD_ERROR);
            return resultData;
        }
    }

    @Override
    public ResultData<String> loginByOpenid(String code) {
        ResultData<String> resultData = new ResultData<>();
        String openid = miniappService.getUserOpenid(code);
        if (openid != null) {
            User user = userMapper.findOneByOpenid(openid);
            if (user != null) {

                // 更新APP注册用户的小程序openid
                if (user.getOpenid() == null) {
                    user.setOpenid(openid);
                    userMapper.updateByPrimaryKeySelective(user);
                }

                // 这里没有验证密码的正确性
                final Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
                SecurityContextHolder.getContext().setAuthentication(authentication);
                final String token = jwtTokenUtil.generateToken(user);
                resultData.setResultCode(ResultCode.SUCCESS);
                resultData.setValue(token);
                // 赠送 开启中的消费金
                CardConsumptionFund card = card(user);
            } else {
                // 用户不存在，无法自动登录，请先注册
                resultData.setResultCode(ResultCode.FAIL);
            }

        } else {
            resultData.setResultCode(ResultCode.GET_OPENID_FAIL);
        }
        return resultData;
    }

    @Override
    public ResultData<String> refresh(String oldToken) {
        ResultData<String> resultData = new ResultData<>();
        final String token = oldToken.substring(tokenHead.length());
        String username = jwtTokenUtil.getUsernameFromToken(token);
        User user = (User) userDetailsService.loadUserByUsername(username);
        if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())) {
            resultData.setValue(jwtTokenUtil.refreshToken(token));
            // 赠送消费金
            card(user);
            return resultData;
        }
        resultData.setResultCode(ResultCode.FAIL);
        return resultData;
    }

    @Override
    public ResultData<String> getKaptchaImage(HttpServletResponse response, HttpSession session) {
        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");

        String capText = defaultKaptcha.createText();
        //capText是由图形验证码随机生成的4个字符
        System.out.println("capText: " + capText);

        Long time = System.currentTimeMillis();
        try {
            session.setAttribute("captchaKey", time.toString() + "," + capText);
        } catch (Exception e) {
            e.printStackTrace();
        }

        BufferedImage bi = defaultKaptcha.createImage(capText);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(bi, "jpg", out);
        } catch (IOException e) {
            e.printStackTrace();
        }
        byte[] bytes = out.toByteArray();
        Base64.Encoder base64Encoder = Base64.getEncoder();
        return new ResultData<>(base64Encoder.encodeToString(bytes));
    }

    @Override
    public BaseResp updatePasswordByPhone(UpdatePasswordByPhoneReq req) {
        User user = userMapper.findOneByUsername(req.getPhone());

        if (user == null) {
            return new BaseResp(ResultCode.USER_NOT_EXISTS);
        }

        if (!smsService.verifySms(req.getPhone(), req.getCode())) {
            return new BaseResp(ResultCode.SMS_CODE_WRONG);
        }

        // 写入新密码
        User updateUser = new User();
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        final String rawPassword = req.getNewPassword();
        updateUser.setId(user.getId());
        updateUser.setPassword(encoder.encode(rawPassword));
        updateUser.setLastPasswordResetDate(new Date());
        updateUser.setUpdatedAt(new Date());
        userMapper.updateByPrimaryKeySelective(updateUser);

        return new BaseResp();
    }

    @Override
    public ResultData<String> wxRegister(RegisterReq req) {
        ResultData<String> resultData = new ResultData<>();
        WxMaJscode2SessionResult session = miniappService.getUserSession(req.getJscode());
        WxMaPhoneNumberInfo wxMaPhoneNumberInfo = miniappService.getUserPhone(session.getSessionKey(), req.getEncryptedData(), req.getIv());

        User user = new User();
        user.setOpenid(session.getOpenid());
        if (wxMaPhoneNumberInfo.getPhoneNumber() == null) {
            resultData.setResultCode(ResultCode.FAIL);
            return resultData;
        }

        // 判断是否已经在APP注册了
        User result = userMapper.findOneByUsername(wxMaPhoneNumberInfo.getPhoneNumber());
        if (result != null && result.getOpenid() == null) {
            result.setOpenid(session.getOpenid());
            userMapper.updateByPrimaryKeySelective(result);

            // 这里没有验证密码的正确性
            final Authentication authentication = new UsernamePasswordAuthenticationToken(result.getUsername(), result.getPassword());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            final String token = jwtTokenUtil.generateToken(result);
            resultData.setResultCode(ResultCode.SUCCESS);
            resultData.setValue(token);
            return resultData;
        }


        req.setPassword(UUID.randomUUID().toString());
        user.setUsername(wxMaPhoneNumberInfo.getPhoneNumber());
        user.setFromChannel(FromChannelEnum.MINI_PROGRAM.getType());
        return commonRegister(user, req);
    }

    @Override
    public ResultData<String> appRegister(RegisterReq req) {
        ResultData<String> resultData = new ResultData<>();
        User user = new User();

        // 验证码是否正确
        if (!smsService.verifySms(req.getUsername(), req.getCode())) {
            resultData.setResultCode(ResultCode.SMS_CODE_WRONG);
            return resultData;
        }

        user.setUsername(req.getUsername());
        user.setFromChannel(FromChannelEnum.APP.getType());
        return commonRegister(user, req);
    }

    @Override
    public ResultData<String> getPhoneStatus(String phone) {
        User user = userMapper.findOneByUsername(phone);
        if (user == null) {
            return new ResultData<>("NOT_REGISTER");
        } else {
            if (user.getOpenid() == null) {
                // 仅APP注册
                return new ResultData<>("REGISTER_APP");
            }

            // 已完整注册
            return new ResultData<>("REGISTER");
        }
    }


    @Override
    public BaseResp updatePayPassword(String payPassword, String code) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        // 验证码是否正确
        if (!smsService.verifySms(user.getUsername(), code)) {
            return new BaseResp(ResultCode.SMS_CODE_WRONG);
        }

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        userMapper.updatePayPasswordById(encoder.encode(payPassword), user.getId());
        return new BaseResp();
    }

    @Override
    public BaseResp loginByPayPassword(String payPassword, Byte type) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (user.getPayPassword() == null) {
            return new BaseResp(ResultCode.NOT_HAS_PAY_PASSWORD);
        }
        if (!new BCryptPasswordEncoder().matches(payPassword, user.getPayPassword())) {
            return new BaseResp(ResultCode.PAY_PASSWORD_ERROR);
        }

        ValueOperations<String, Boolean> operations = redisTemplate.opsForValue();


        // 进行商家登录
        if (PayLoginTypeEnum.SHOP.getType().equals(type)) {

            if (user.getShopId() == null) {
                return new BaseResp(ResultCode.DO_NOT_HAVE_SHOP);
            }

            String key = "SHOP_SECURITY_LOGIN:" + user.getId();
            operations.set(key, true, 1, TimeUnit.DAYS);
            return new BaseResp();
        }

        if (PayLoginTypeEnum.AREA.getType().equals(type)) {

            List<UserAgentLevel> userAgentLevels = userAgentLevelMapper.findAllByUserIdAndExpireTime(user.getId(), new Date());

            // 允许非区域代理登录查看，以便进行申请
//            if (userAgentLevels.isEmpty()) {
//                return new BaseResp(ResultCode.DO_NOT_HAVE_AREA);
//            }

            String key = "AREA_SECURITY_LOGIN:" + user.getId();
            operations.set(key, true, 1, TimeUnit.DAYS);
            return new BaseResp();
        }

        return new BaseResp();
    }

    @Override
    public ResultData<Boolean> isPayLogin(User user, Byte type) {
        if (user == null) {
            ResultData<Boolean> resultData = new ResultData<>(false);
            resultData.setResultCode(ResultCode.NOT_LOGIN);
            return resultData;
        }
        ValueOperations<String, Boolean> operations = redisTemplate.opsForValue();

        if (PayLoginTypeEnum.SHOP.getType().equals(type)) {
            String key = "SHOP_SECURITY_LOGIN:" + user.getId();
            return new ResultData<>(operations.get(key));
        }

        if (PayLoginTypeEnum.AREA.getType().equals(type)) {
            String key = "AREA_SECURITY_LOGIN:" + user.getId();
            return new ResultData<>(operations.get(key));
        }
        return new ResultData<>(false);
    }
}